﻿using System;
using System.Windows;
using System.Linq.Expressions;

namespace MvvmHelper
{
    /// <summary>
    /// System.Windows.PropertyChangedCallback を型安全にしたもの。
    /// </summary>
    /// <typeparam name="TOwner">依存プロパティを所有するクラス。</typeparam>
    /// <typeparam name="TProperty">依存プロパティの型。</typeparam>
    /// <param name="sender">イベントの送信元。</param>
    /// <param name="e">イベント引数。</param>
    public delegate void PropertyChangedCallback<TOwner, TProperty>(TOwner sender, DependencyPropertyChangedEventArgs<TProperty> e) where TOwner : DependencyObject;

    /// <summary>
    /// System.Windows.CoerceValueCallback を型安全にしたもの。
    /// </summary>
    /// <typeparam name="TOwner">依存プロパティを所有するクラス。</typeparam>
    /// <typeparam name="TProperty">依存プロパティの型。</typeparam>
    /// <param name="sender">イベントの送信元。</param>
    /// <param name="baseValue">強制したい値。</param>
    /// <returns>強制結果。</returns>
    public delegate TProperty CoerceValueCallback<TOwner, TProperty>(TOwner sender, TProperty baseValue) where TOwner : DependencyObject;

    /// <summary>
    /// 型安全に DependencyProperty.Register するためのヘルパークラス。
    /// </summary>
    /// <typeparam name="TControl">オーナーの型。</typeparam>
    /// <typeparam name="TProperty">プロパティの型。</typeparam>
    public class DependencyPropertyHelper<TOwner>
            where TOwner : DependencyObject
    {
        /// <summary>
        /// プロパティ名だけ指定して依存プロパティを登録。
        /// </summary>
        /// <param name="propertyExpression">プロパティ名を型安全に取得するための式。</param>
        /// <returns>依存プロパティ。</returns>
        public static DependencyProperty Register<TProperty>(
            Expression<Func<TOwner, TProperty>> propertyExpression
            )
        {
            return Register(propertyExpression, default(TProperty));
        }

        /// <summary>
        /// プロパティ名とデフォルト値を指定して依存プロパティを登録。
        /// </summary>
        /// <param name="propertyExpression">プロパティ名を型安全に取得するための式。</param>
        /// <param name="defaultValue">デフォルト値。</param>
        /// <returns>依存プロパティ。</returns>
        public static DependencyProperty Register<TProperty>(
            Expression<Func<TOwner, TProperty>> propertyExpression,
            TProperty defaultValue
            )
        {
            return DependencyProperty.Register(
                PropertyName<TOwner>.Get(propertyExpression),
                typeof(TProperty),
                typeof(TOwner),
                new UIPropertyMetadata(defaultValue)
                );
        }

        /// <summary>
        /// プロパティ名、デフォルト値、値変化時のコールバックを指定して依存プロパティを登録。
        /// </summary>
        /// <param name="propertyExpression">プロパティ名を型安全に取得するための式。</param>
        /// <param name="defaultValue">デフォルト値。</param>
        /// <param name="propertyChangedCallback">値変化時のコールバック</param>
        /// <returns>依存プロパティ。</returns>
        public static DependencyProperty Register<TProperty>(
            Expression<Func<TOwner, TProperty>> propertyExpression,
            TProperty defaultValue,
            PropertyChangedCallback<TOwner, TProperty> propertyChangedCallback
            )
        {
            return DependencyProperty.Register(
                PropertyName<TOwner>.Get(propertyExpression),
                typeof(TProperty),
                typeof(TOwner),
                new UIPropertyMetadata(
                    defaultValue,
                    (sender, e) =>
                    {
                        propertyChangedCallback(
                            sender as TOwner,
                            new DependencyPropertyChangedEventArgs<TProperty>(e.Property, (TProperty)e.OldValue, (TProperty)e.NewValue));
                    })
                    );
        }

        /// <summary>
        /// プロパティ名、デフォルト値、値変化時のコールバック、値強制コールバックを指定して依存プロパティを登録。
        /// </summary>
        /// <param name="propertyExpression">プロパティ名を型安全に取得するための式。</param>
        /// <param name="defaultValue">デフォルト値。</param>
        /// <param name="propertyChangedCallback">値変化時のコールバック</param>
        /// <param name="coerceValueCallback">値強制コールバック</param>
        /// <returns>依存プロパティ。</returns>
        public static DependencyProperty Register<TProperty>(
            Expression<Func<TOwner, TProperty>> propertyExpression,
            TProperty defaultValue,
            PropertyChangedCallback<TOwner, TProperty> propertyChangedCallback,
            CoerceValueCallback<TOwner, TProperty> coerceValueCallback
            )
        {
            return DependencyProperty.Register(
                PropertyName<TOwner>.Get(propertyExpression),
                typeof(TProperty),
                typeof(TOwner),
                new UIPropertyMetadata(
                    defaultValue,
                    (sender, e) =>
                    {
                        propertyChangedCallback(
                            sender as TOwner,
                            new DependencyPropertyChangedEventArgs<TProperty>(e.Property, (TProperty)e.OldValue, (TProperty)e.NewValue));
                    },
                    (sender, baseValue) =>
                    {
                        return coerceValueCallback(
                            sender as TOwner,
                            (TProperty)baseValue);
                    })
                    );
        }
    }
}
